django-structlog 4.0未満での middlewareの注意点
code:settings.py
MIDDLEWARE = [
# ...
'django_structlog.middlewares.RequestMiddleware',
]
middlewareの実装では logger.bind(user_id=user_id) としてい
code:python
@staticmethod
def bind_user_id(request):
if hasattr(request, "user") and request.user is not None:
user_id = request.user.pk
if isinstance(user_id, uuid.UUID):
user_id = str(user_id)
logger.bind(user_id=user_id)
上記は context_class=structlog.threadlocal.wrap_dict(dict) に依存する実装になっているが、これは logger.bind の値を暗黙的にthreadlocalに共有する。
この設定では、以下の不都合が発生する
code:python
logger.info('message') # -> {'event': 'message'}
logger2 = logger.bind(foo='追加情報')
logger2.info('message') # -> {'event': 'message', 'foo': '追加情報'}
logger.info('message') # -> {'event': 'message', 'foo': '追加情報'}
# 追加情報fooは元のloggerの方では出力されないはずだったのに、出力されてしまってる
logger3 = structlog.get_logger()
logger3.info('message') # -> {'event': 'message', 'foo': '追加情報'}
# 別モジュールのloggerでも出力されてしまう
これでは局所的なbindができない
structlog.threadlocal.wrap_dict は、structlog本体で 2021/07/16 に非推奨となった 対策として、以下の2つを行うとよい
1. settings で context_class を指定しない
2. RequestMiddleware クラスを継承し、 bind_user_id を以下の実装に差し替える
code:python
from django_structlog.middlewares import RequestMiddleware
from structlog.threadlocal import bind_threadlocal
class RequestLogMiddleware(RequestMiddleware):
@staticmethod
def bind_user_id(request):
if hasattr(request, "user") and request.user is not None:
user_id = request.user.pk
if isinstance(user_id, uuid.UUID):
user_id = str(user_id)
bind_threadlocal(user_id=user_id) # この行を変更